﻿////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// <copyright>Copyright 2008-2011 Andreas Huber Doenni</copyright>
// Distributed under the Microsoft Public License (Ms-PL).
// See accompanying file License.txt or copy at http://mews.codeplex.com/license.
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

namespace Mews.Text
{
    using System;
    using System.Linq;

    using Mews.UnitTesting;
    using Microsoft.VisualStudio.TestTools.UnitTesting;

    /// <summary>Tests the <see cref="IdentityEncoding"/> class.</summary>
    [TestClass]
    public sealed class IdentityEncodingTest : TestBase
    {
        /// <summary>Tests all use cases of the <see cref="IdentityEncoding"/> class.</summary>
        [TestMethod]
        public void AllTest()
        {
            var encoding = new IdentityEncoding();

            this.TestGetMaxCount(encoding.GetMaxByteCount);
            this.TestGetMaxCount(encoding.GetMaxCharCount);

            this.TestGetCount<char>(encoding.GetByteCount);
            this.TestGetCount<byte>(encoding.GetCharCount);

            this.TestGetArray(encoding);

            var unmappable = (from code in Enumerable.Range(byte.MaxValue + 1, char.MaxValue - byte.MaxValue)
                              select (char)code).ToArray();
            var encoded = encoding.GetBytes(unmappable);
            Assert.AreEqual(0, encoded.SkipWhile(val => val == (byte)'?').Count());
        }

        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////

        private void TestGetMaxCount(Func<int, int> getMaxCount)
        {
            int randomCount = this.Random.Next(0, int.MaxValue);
            Assert.AreEqual(randomCount, getMaxCount(randomCount));
        }

        private void TestGetCount<T>(Func<T[], int, int, int> getCount)
        {
            int randomIndex;
            int randomCount;
            T[] randomArray = this.GetRandomArray<T>(out randomIndex, out randomCount);
            Assert.AreEqual(randomCount, getCount(randomArray, randomIndex, randomCount));
            AssertThrow<ArgumentNullException>(() => getCount(null, randomIndex, randomCount));
            AssertThrow<ArgumentOutOfRangeException>(
                () => getCount(randomArray, -1, randomCount),
                () => getCount(randomArray, randomIndex, -1));
            AssertThrow<ArgumentException>(
                () => getCount(randomArray, randomIndex, randomArray.Length - randomIndex + 1));
        }

        private void TestGetArray(IdentityEncoding encoding)
        {
            int originalIndex;
            int originalCount;
            var original = GetRandomArray<byte>(out originalIndex, out originalCount);
            this.Random.NextBytes(original);
            Array.Clear(original, 0, originalIndex);
            Array.Clear(original, originalIndex + originalCount, original.Length - originalIndex - originalCount);

            var decoded = new char[originalCount];
            Assert.AreEqual(originalCount, encoding.GetChars(original, originalIndex, originalCount, decoded, 0));
            var copy = new byte[original.Length];
            Assert.AreEqual(decoded.Length, encoding.GetBytes(decoded, 0, decoded.Length, copy, originalIndex));
            CollectionAssert.AreEqual(original, copy);

            TestGetArray<char, byte>(encoding.GetBytes);
            TestGetArray<byte, char>(encoding.GetChars);
        }

        private T[] GetRandomArray<T>(out int index, out int count)
        {
            var array = new T[this.Random.Next(1024)];
            index = this.Random.Next(0, array.Length);
            count = this.Random.Next(0, array.Length - index);
            return array;
        }

        private delegate int GetArray<TFirst, TSecond>(
            TFirst[] first, int firstIndex, int firstCount, TSecond[] second, int secondIndex);

        private void TestGetArray<TFirst, TSecond>(GetArray<TFirst, TSecond> getArray)
        {
            int originalIndex;
            int originalCount;
            var original = GetRandomArray<TFirst>(out originalIndex, out originalCount);
            var copy = new TSecond[originalCount];
            Assert.AreEqual(originalCount, getArray(original, originalIndex, originalCount, copy, 0));

            AssertThrow<ArgumentNullException>(
                () => getArray(null, originalIndex, originalCount, copy, 0),
                () => getArray(original, originalIndex, originalCount, null, 0));
            AssertThrow<ArgumentOutOfRangeException>(
                () => getArray(original, -1, originalCount, copy, 0),
                () => getArray(original, originalIndex, -1, copy, 0),
                () => getArray(original, originalIndex, originalCount, copy, -1));
            AssertThrow<ArgumentException>(
                () => getArray(original, originalIndex, original.Length - originalIndex + 1, copy, 0),
                () => getArray(original, originalIndex, originalCount, copy, 1));
        }
    }
}
